package jsonrpc

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"reflect"
	"runtime/debug"
	"sync"
)

var (
	typeOfError  = reflect.TypeOf((*error)(nil)).Elem()
	errEmptyBody = errors.New("empty json body")
)

func NewServerWithCodec(codec ServerCodec) *Server {
	if codec == nil {
		codec = NewJSONCodec()
	}
	return &Server{codec: codec}
}

type Auth struct {
	AuthState     bool                 //是否认证
	NoAuthMethod  []string             //不需要认证字符串
	AuthCheckFunc func(as string) bool //认证方法
}

type AuthRepy struct {
	AuthStr string
	UserId  string
}

// Server data struct to serve RPC request over TCP and HTTP
type Server struct {
	m         sync.Map    // map[string]*service
	codec     ServerCodec // codec to read request and writeResponse
	AuthCheck *Auth       //认证检测
}

func (s *Server) call(reqs []jsonRequest) (replies []jsonResponse) {
	defer func() { Log.Debug("server called end") }()
	replies = make([]jsonResponse, len(reqs))
	for idx, req := range reqs {
		var (
			reply jsonResponse
		)
		//认证逻辑
		authr := new(AuthRepy)
		if s.AuthCheck != nil && s.AuthCheck.AuthState {
			authOk := false
			fmt.Println("进入认证逻辑..")
			for _, authStr := range s.AuthCheck.NoAuthMethod {
				if authStr == req.Method() {
					authOk = true
					break
				}
			}
			if !authOk && !s.AuthCheck.AuthCheckFunc(req.Auth) {
				reply = s.codec.ErrResponse(AuthCheckErr, errors.New("AuthCheckErr"))
				replies[idx] = reply
				fmt.Println("认证错误")
				continue
			}
		}
		//增加认证返回参数
		authr.AuthStr = req.Auth
		serviceName, methodName, err := parseFromRPCMethod(req.Method())
		if err != nil {
			Log.Debug("parseFromRPCMethod err=%v", err)
			reply = s.codec.ErrResponse(InvalidRequest, err)
			replies[idx] = reply
			continue
			// goto errSkip
		}

		// method existed or not
		//查找名称(如user.login中的user)
		svci, ok := s.m.Load(serviceName)
		if !ok {
			err := errors.New("rpc: can't find service " + serviceName)
			reply = s.codec.ErrResponse(MethodNotFound, err)
			replies[idx] = reply
			continue
		}

		svc := svci.(*service)
		//查找需要调用的方法(如user.login 中的login)
		fmt.Println("method:",svc.method)
		mtype := svc.method[methodName]
		if mtype == nil {
			err := errors.New("rpc: can't find method " + req.Method())
			reply = s.codec.ErrResponse(MethodNotFound, err)
			replies[idx] = reply
			continue
		}

		// To prepare argv and replyv in reflect.Value. Refer to `net/http/rpc`
		// If true, need to indirect before calling.
		var (
			argv       reflect.Value
			argIsValue = false
		)
		//栗子 type Foo struct {}， 那么Kind就是struct,  Type就是Foo
		if mtype.ArgType.Kind() == reflect.Ptr {
			argv = reflect.New(mtype.ArgType.Elem())
		} else {
			argv = reflect.New(mtype.ArgType)
			argIsValue = true
		}
		if argIsValue {
			argv = argv.Elem() // argv guaranteed to be a pointer now.
		}

		if err := s.codec.ReadRequestBody(req.Params(), argv.Interface()); err != nil {
			Log.Debug("could not readRequestBody err=", err)
			err := errors.New("rpc: could not read request body " + req.Method())
			reply = s.codec.ErrResponse(InternalErr, err)
			replies[idx] = reply
			continue
		}

		var replyv reflect.Value
		replyv = reflect.New(mtype.ReplyType.Elem())
		switch mtype.ReplyType.Elem().Kind() {
		case reflect.Map:
			replyv.Elem().Set(reflect.MakeMap(mtype.ReplyType.Elem()))
		case reflect.Slice:
			replyv.Elem().Set(reflect.MakeSlice(mtype.ReplyType.Elem(), 0, 0))
		}
		authv := reflect.ValueOf(authr)
		if err := svc.call(mtype, argv, replyv, authv); err != nil {
			reply = s.codec.ErrResponse(InternalErr, err)
		} else {
			// normal response
			reply = s.codec.NewResponse(replyv.Interface())
			reply.SetReqIdent(req.Ident())
		}

		replies[idx] = reply
	}
	// for req range reqs. END

	return
}

func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err, ok := recover().(error); ok && err != nil {
			// log.Printf("%v", err)
			Log.Debug("[ServeHTTP] recover %v with stack: \n", err)
			debug.PrintStack()
		}
	}()

	var (
		data []byte
		err  error
	)
	//请求类型判断
	switch req.Method {
	//如果post类型，则读取请求内容。并判断是否有错误
	case http.MethodPost:
		if data, err = ioutil.ReadAll(req.Body); err != nil {
			resp := s.codec.ErrResponse(InvalidParamErr, err)
			byts, err := s.codec.EncodeResponses(resp)
			Log.Error("s.codec.EncodeResponses err=", err)
			String(w, http.StatusOK, byts)
			// JSON(w, http.StatusOK, resp)

			return
		}
		defer req.Body.Close()
	//如果不是Post类型，直接返回错误
	default:
		err := errors.New("method not allowed: " + req.Method)
		resp := s.codec.ErrResponse(MethodNotFound, err)
		// JSON(w, http.StatusOK, resp)
		byts, err := s.codec.EncodeResponses(resp)
		if err != nil {
			Log.Debug("s.codec.EncodeResponses err=%v", err)
		}

		String(w, http.StatusOK, byts)
		return
	}
	Log.Debug("[HTTP] got request data", string(data))
	//json解码
	rpcReqs, err := s.codec.ReadRequest([]byte(data))
	if err != nil {
		resp := s.codec.ErrResponse(ParseErr, err)
		// JSON(w, http.StatusOK, resp)
		byts, err := s.codec.EncodeResponses(resp)
		if err != nil {
			Log.Debug("s.codec.EncodeResponses err=", err)
		}
		String(w, http.StatusOK, byts)
		s.codec = nil
		s.codec = NewJSONCodec()
		return
	}

	resps := s.call(rpcReqs)
	Log.Debug("s.call(rpcReq) result:", resps)
	// JSON(w, http.StatusOK, resps)
	byts, err := s.codec.EncodeResponses(resps)
	if err != nil {
		Log.Error("s.codec.EncodeResponses err=", err)
	} else {
		Log.Debug("s.codec.EncodeResponses编码完成")
	}
	String(w, http.StatusOK, byts)
	return
}

// String .
func String(w http.ResponseWriter, statusCode int, byts []byte) error {
	// w.Header().Set("Content-Type", "text/plain")
	w.Header().Set("Content-Type", "application/json-rpc")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
	w.WriteHeader(statusCode)
	// byts, err := json.Marshal(v)
	// if err != nil {
	// 	DebugF("could not marshal v=%v, err=%v", v, err)
	// 	return err
	// }

	_, err := io.WriteString(w, string(byts))
	return err
}

func (s *Server) Register(rcvr interface{}) error {
	srvic := new(service)
	srvic.typ = reflect.TypeOf(rcvr)
	srvic.rcvr = reflect.ValueOf(rcvr)
	fmt.Println("srvic.typ:", srvic.typ)
	fmt.Println("srvic.rcvr:", srvic.rcvr)
	sname := reflect.Indirect(srvic.rcvr).Type().Name()
	fmt.Println("sname:",sname)
	if sname == "" {
		errmsg := "rpc.Register: no service name for type " + srvic.typ.String()
		Log.Debug(errmsg)
		return errors.New(errmsg)
	}

	if !isExported(sname) {
		errmsg := "rpc.Register: type " + sname + " is not exported"
		Log.Debug(errmsg)
		return errors.New(errmsg)
	}
	srvic.name = sname
	fmt.Println("srvic.name:", srvic.name)
	srvic.method = suitableMethods(srvic.typ)
	fmt.Println("srvic.method:",srvic.method)
	if _, dup := s.m.LoadOrStore(sname, srvic); dup {
		return errors.New("rpc: service already defined: " + sname)
	}
	return nil
}
